package com.yc.sole.framework.redis;

import cn.hutool.crypto.SecureUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.yc.sole.framework.exception.BaseException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.web.multipart.MultipartFile;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * 防重复提交
 *
 * @author yizuomin
 * @date 2023/2/28 15:47
 **/
@Aspect
@Slf4j
@RequiredArgsConstructor
public class RepeatSubmitAspect {

    private final RedissonService redissonService;
    private final ObjectMapper objectMapper;

    /**
     * 防重复提交aop
     *
     * @param joinPoint
     * @return
     * @throws Throwable
     */
    @Around("@annotation(com.yc.sole.framework.redis.RepeatSubmitCheck)")
    public Object repeatSubmitCheck(ProceedingJoinPoint joinPoint) throws Throwable {
        Signature signature = joinPoint.getSignature();
        MethodSignature msg = (MethodSignature) signature;
        Object target = joinPoint.getTarget();
        Method method = target.getClass().getMethod(msg.getName(), msg.getParameterTypes());
        RepeatSubmitCheck annotation = method.getAnnotation(RepeatSubmitCheck.class);
        //参数
        Object[] args = joinPoint.getArgs();
        //将请求参数转为字符串并加密
        ParameterNameDiscoverer pnd = new DefaultParameterNameDiscoverer();
        String[] parameterNames = pnd.getParameterNames(method);
        Map<String, Object> paramsMap = getParamsMap(parameterNames, args);
        String secretKey = SecureUtil.md5(objectMapper.writeValueAsString(paramsMap));
        //利用原子自增保证防止重复提交 指定时间内再次提交会被认为是重复提交
        long increment = redissonService.incrementLongAndExpire(secretKey, annotation.expireTime());
        if (increment > 1) {
            throw new BaseException("操作过于频繁，请稍后再试！"); // 重复点击
        }
        Object result = null;
        try {
            result = joinPoint.proceed();
        } catch (Exception e) {
            log.error(e.getMessage());
            throw new BaseException(e.getMessage());
        }
        return result;
    }

    /**
     * 通过map封装参数和参数值
     *
     * @param parameterNames parameterNames
     * @param args           args
     * @return map
     */
    private static Map<String, Object> getParamsMap(String[] parameterNames, Object[] args) {
        Map<String, Object> paramMap = new HashMap<>(Objects.requireNonNull(parameterNames).length);
        for (int i = 0; i < Objects.requireNonNull(parameterNames).length; i++) {
            Object arg = args[i];
            if (arg instanceof HttpServletRequest
                    || arg instanceof HttpServletResponse
                    || arg instanceof MultipartFile) {
                arg = args.getClass().getSimpleName();
            }
            paramMap.put(parameterNames[i], arg);
        }
        return paramMap;
    }
}
